<?php
/**
 * @package common
 */

/**
 * Class which provides framework writing to a file.
 */
class Writer {
	/** File name. */
	private $fileName;
	/** Resource */
	private $resource;
	
	/**
	 * Create instance of Write class
	 */
	protected function __construct(string $fileName) {
		$this->fileName = $fileName;
	}
	
	/**
	 * Destructor which closes file resource
	 */
	function __destruct() {
		// TODO check if file is still opened
		if (is_resource($this->resource)) $this->closeFile();
	}
	
	/**
	 * Open file.
	 *
	 * @param string $mode
	 * @return resource
	 */
	protected function openFile(string $mode) {
		return $this->resource = fopen($this->fileName, $mode); 
	}
	
	/**
	 * Close file.
	 */
	protected function closeFile() {
		fclose($this->resource);
	}
	
	/**
	 * Write $str to the file.
	 *
	 * @param string $str
	 * @return int - status of fwrite
	 */
	function printTxt(string $str) {
		return fwrite($this->resource, $str);
	}
}

/**
 * Logging framework provider.
 */
class Logger extends Writer {
	/** date pattern */
	const DATE = "/%d/";
	/** time pattern */
	const TIME = "/%t/";
	/** server pattern */
	const SERVER = "/%s/";
	/** client pattern */
	const CLIENT = "/%c/";
	/** log level pattern */
	const LOGLEVEL = "/%l/";
	
	/** Log level error */
	const ERROR_LEVEL = 1;
	/** Log level info */
	const INFO_LEVEL = 2;
	/** Log level debug */
	const DEBUG_LEVEL = 3;
	
	/** Log level */
	private $logLevel;
	/** Lin mask */
	private $lineMask;
	
	/** instance of this class */
	private static $logger;
	
	/**
	 * Create instance of this class.
	 *
	 * @param string $logFile
	 * @param int $logLevel
	 * @param string $lineMask
	 */
	protected function __construct(string $logFile, $logLevel = self::ERROR_LEVEL, $lineMask = "[%l]\t| [%d], [%t] s: %s, c: %c   |   ") {
		parent::__construct($logFile);
		$this->lineMask = $lineMask;
		$this->logLevel = $logLevel;
		
		/** set default timezone */
		date_default_timezone_set("Europe/Prague");
		
		/**
		 * If opened in append mode,
		 * PHP solves atomic write itself.
		 */
		$this->openFile("a");
	}
	
	/**
	 * The only one way to receive instance of this class.
	 * @return Logger
	 */
	public static function getLogger() {
		if (!is_object(self::$logger))
			self::$logger = new Logger(Constants::LOG_FILE, Constants::LOG_LEVELDEFAULT);
		return self::$logger;
	}
	
	/**
	 * Write text into the file.
	 *
	 * @param int $logLevel
	 * @param string $str
	 * @return int - indication of failure
	 */
	private function printTxtEx (int $logLevel, string $str) {
		if ($logLevel > $this->logLevel) return false;
		switch ($logLevel) {
			case 1: $txt = preg_replace(self::LOGLEVEL, "ERROR", $this->lineMask);
			break;
			case 2: $txt = preg_replace(self::LOGLEVEL, "INFO", $this->lineMask);
			break;
			case 3: $txt = preg_replace(self::LOGLEVEL, "DEBUG", $this->lineMask);
			break;
		}
		$str = preg_replace("/\n/", "\n".$txt."\t", $str);
		$txt = $txt . "\t" . $str;
		
		//$txt = preg_replace()
		$txt = preg_replace(self::DATE, date("Y-m-d"), $txt);
		$txt = preg_replace(self::TIME, date("H:i:s"), $txt);
		$txt = preg_replace(self::SERVER, $_SERVER['SERVER_NAME'], $txt);
		$txt = preg_replace(self::CLIENT, $_SERVER['REMOTE_ADDR'], $txt);
		
		return $this->printTxt($txt . "\r\n");
	}
	
	/**
	 * Log on an info level
	 * 
	 * @param string $str - text to log
	 */
	public function info(string $str) {
		return $this->printTxtEx(self::INFO_LEVEL, $str);
	}
	
	/**
	 * Log on an error level
	 * 
	 * @param string $str - text to log
	 */
	public function error(string $str) {
		return $this->printTxtEx(self::ERROR_LEVEL, $str);
	}
	
	/**
	 * Log on an debug level
	 * 
	 * @param string $str - text to log
	 */
	public function debug(string $str) {
		return $this->printTxtEx(self::DEBUG_LEVEL, $str);
	}
	
	/**
	 * Change level of logging.
	 * Remember: ERROR < INFO < DEBUG
	 * @param int $level - Logger::ERROR_LEVEL | Logger::INFO_LEVEL | Logger::DEBUG_LEVEL
	 */
	public static function setLevel(int $level) {
		$instance = self::getLogger();
		$instance->logLevel = $level;
	}
}

?>